package edu.pitt.dbmi.odie.gapp.gwt.client.navigator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

import com.allen_sauer.gwt.log.client.Log;
import com.smartgwt.client.types.Positioning;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.HTMLFlow;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;

public class ODIE_HTMLFlowDocumentViewer extends Canvas implements ClickHandler {

	private static final int STATE_PLAIN_TEXT = 0;
	private static final int STATE_ANNOTATION_TEXT = 1;

	private ArrayList<Canvas> widgets = new ArrayList<Canvas>();

	private int currentLine = -1;

	private ArrayList<HTMLFlow> htmlFragments = new ArrayList<HTMLFlow>();

	private ODIE_NavigatableAnnotation currentAnnotation = null;

	private int currentAnnotationIndex = 0;

	private ODIE_NavigatorPanel navigatorPanel;

	public ODIE_NavigatorPanel getNavigatorPanel() {
		return navigatorPanel;
	}

	public void setNavigatorPanel(ODIE_NavigatorPanel navigatorPanel) {
		this.navigatorPanel = navigatorPanel;
	}

	private static final Comparator<ODIE_NavigatableAnnotation> myComparator = new Comparator<ODIE_NavigatableAnnotation>() {
		public int compare(ODIE_NavigatableAnnotation o1,
				ODIE_NavigatableAnnotation o2) {
			int ret = 0;
			ret = o1.getStartOffset() - o2.getStartOffset();
			// ret = (ret == 0) ? o1.getEndOffset() - o2.getEndOffset() : ret ;
			return ret;
		}
	};

	private static final Comparator<ODIE_NavigatableAnnotation> overlapComparator = new Comparator<ODIE_NavigatableAnnotation>() {
		public int compare(ODIE_NavigatableAnnotation o1,
				ODIE_NavigatableAnnotation o2) {
			int ret = o1.getStartOffset() - o2.getStartOffset();
			if ((o1.getStartOffset() <= o2.getStartOffset() && o1
					.getEndOffset() >= o2.getStartOffset())
					|| (o1.getEndOffset() >= o2.getStartOffset() && o1
							.getEndOffset() <= o2.getEndOffset())) {
				ret = 0;
			}
			return ret;
		}
	};

	public ODIE_HTMLFlowDocumentViewer() {
		this.setHeight("75%");
		this.setWidth("80%");
	}

	public boolean remove(Canvas child) {
		return widgets.remove(child);
	}

	public Iterator<Canvas> iterator() {
		return widgets.iterator();
	}

	public void convertDocument(String documentText,
			ArrayList<ODIE_NavigatableAnnotation> annotations) {
		TreeSet<ODIE_NavigatableAnnotation> sortedAnnotations = new TreeSet<ODIE_NavigatableAnnotation>(
				overlapComparator);
		sortedAnnotations.addAll(annotations);
		annotations.clear();
		annotations.addAll(sortedAnnotations);
		ODIE_NavigatableDocument docToConvert = new ODIE_NavigatableDocument();
		docToConvert.setText(documentText);
		docToConvert.setAnnotations(annotations);
		annotateAndConvertDocument(docToConvert);
	}

	protected void annotateAndConvertDocument(
			ODIE_NavigatableDocument navigatableDoc) {

		Canvas htmlBuilderPanel = new Canvas();
		htmlBuilderPanel.setWidth100();
		htmlBuilderPanel.setHeight100();

		// htmlBuilderPanel.addStyleName("margin-top:5px");
		// htmlBuilderPanel.addStyleName("margin-bottom:5px");
		this.currentLine = 0;
		int readingState = STATE_PLAIN_TEXT;
		this.currentAnnotation = null;
		Collection<ODIE_NavigatableAnnotation> annotations = navigatableDoc
				.getAnnotations();
		Iterator<ODIE_NavigatableAnnotation> annotationsIterator = null;
		if (annotations != null && annotations.size() > 0) {
			annotationsIterator = navigatableDoc.getAnnotations().iterator();
			currentAnnotation = (ODIE_NavigatableAnnotation) annotationsIterator
					.next();
		}
		// Log.debug(navigatableDoc.getText());
		char[] documentChars = navigatableDoc.getText().toCharArray();
		int currentCharacterIndex = 0;
		ArrayList<Character> htmlBuffer = new ArrayList<Character>();
		this.htmlFragments.clear();
		appendHtml(htmlBuffer, "<span class='odie-DocumentText'>");
		while (currentCharacterIndex < documentChars.length) {
			char currentCharacter = documentChars[currentCharacterIndex];
			if (readingState == STATE_PLAIN_TEXT) {
				if (currentAnnotation != null
						&& currentCharacterIndex == currentAnnotation
								.getStartOffset()) {
					appendHtml(htmlBuffer, "</span>");
					// htmlBuilderPanel.addChild(flushBufferToHTML(htmlBuffer,
					// "odie-DocumentText"));
					currentAnnotation.setLineOffset(currentLine);
					String idPart = "id="
							+ this.currentAnnotation.deriveHtmlIdPart();
					String linkHtmlString = "<a ";
					linkHtmlString += idPart;
					linkHtmlString += " class='odie-Annotation'";
					linkHtmlString += " href='javascript:;'";
					// linkHtmlString += " text-decoration='none' ";
					// String annotationBackgroundColor = (currentAnnotation
					// .getColor() == null) ? "#CA1F7B"
					// : currentAnnotation.getColor();
					// linkHtmlString += " background-color='" +
					// annotationBackgroundColor + "' " ;
					// linkHtmlString += " background="
					// + annotationBackgroundColor + " ";
					linkHtmlString += " >";
					// Log.debug("Annotation link ==> " +
					// linkHtmlString) ;
					appendHtml(htmlBuffer, linkHtmlString);
					readingState = STATE_ANNOTATION_TEXT;
				}
			}
			if (readingState == STATE_ANNOTATION_TEXT) {
				if (this.currentAnnotation != null
						&& currentCharacterIndex == this.currentAnnotation
								.getEndOffset() + 1) {
					appendHtml(htmlBuffer, "</a>");
					// HTMLFlow annotationHyperLink =
					// flushBufferToHTML(htmlBuffer,
					// "odie-Annotation");
					// annotationHyperLink.addClickHandler(this);
					// this.htmlFragments.add(annotationHyperLink);
					// htmlBuilderPanel.addChild(annotationHyperLink);
					if (annotationsIterator.hasNext()) {
						currentAnnotation = (ODIE_NavigatableAnnotation) annotationsIterator
								.next();
					}
					String linkHtmlString = "<span class='odie-DocumentText'>";
					appendHtml(htmlBuffer, linkHtmlString);
					readingState = STATE_PLAIN_TEXT;
				}
			}
			if (currentCharacter == '\n') {
				this.currentLine++;
				appendHtml(htmlBuffer, "<br>");
			} else if (currentCharacter == ' ') {
				appendHtml(htmlBuffer, "&nbsp;");
			} else {
				htmlBuffer.add(new Character(currentCharacter));
			}
			currentCharacterIndex++;
		}
		// Log.debug(flushBufferToString(htmlBuffer));
		if (readingState == STATE_PLAIN_TEXT && htmlBuffer.size() > 0) {
			appendHtml(htmlBuffer, "</span>");
		}
		HTMLFlow htmlDOMElement = flushBufferToHTML(htmlBuffer,
				"odie-DocumentText");
		inspectHTMLFlow(htmlDOMElement) ;
		htmlDOMElement.addClickHandler(this);
		htmlBuilderPanel.addChild(htmlDOMElement);
		addChild(htmlBuilderPanel);
	}

	protected void inspectHTMLFlow(HTMLFlow htmlDOMElement) {
		Canvas[] children = htmlDOMElement.getChildren();
		if (children != null) {
			for (int idx = 0; idx < children.length; idx++) {
				Canvas child = children[idx];
			}
		}
	}

	protected HTMLFlow flushBufferToHTML(ArrayList<Character> htmlBuffer,
			String styleName) {
		String result = "";
		for (Iterator<Character> resultIterator = htmlBuffer.iterator(); resultIterator
				.hasNext();) {
			result += resultIterator.next();
		}
		htmlBuffer.clear();
		HTMLFlow resultHTML = new HTMLFlow(result);
		resultHTML.setPosition(Positioning.RELATIVE);
		// resultHTML.setStyleName(styleName);
		// resultHTML.setStylePrimaryName(styleName);

		if (styleName.startsWith("odie-Annotation")
				&& this.currentAnnotation != null) {
			String annotationBackgroundColor = (currentAnnotation.getColor() == null) ? "#CA1F7B"
					: currentAnnotation.getColor();
			if (!annotationBackgroundColor.startsWith("#")) {
				annotationBackgroundColor = "#" + annotationBackgroundColor;
			}
			// resultHTML.setBackgroundColor(annotationBackgroundColor) ;
		}
		if (styleName.toLowerCase().endsWith("selected")) {
			resultHTML.setBorder("5px solid black");
		}
		Log.warn(resultHTML.getContents());
		return resultHTML;
	}

	protected ArrayList<Character> appendHtml(ArrayList<Character> htmlBuffer,
			String html) {
		char[] htmlArray = html.toCharArray();
		for (int idx = 0; idx < htmlArray.length; idx++) {
			htmlBuffer.add(new Character(htmlArray[idx]));
		}
		return htmlBuffer;
	}

	protected String flushBufferToString(ArrayList<Character> htmlBuffer) {
		String result = "";
		String breakBuffer = "";
		for (Iterator<Character> resultIterator = htmlBuffer.iterator(); resultIterator
				.hasNext();) {
			char c = ((Character) resultIterator.next()).charValue();
			breakBuffer += c;
			result += c;
			if (breakBuffer.equalsIgnoreCase("<br>")) {
				result += "\n";
				breakBuffer = "";
			}
		}
		return result;
	}

	public void onClick(ClickEvent event) {
		Log.debug("Got click event at " + event.getX() + ", " + event.getY()) ;
//		getAnnotationForWidget((HTMLFlow) event.getSource());
//		this.navigatorPanel
//				.positionAnnotation(extractAnnotationIdFromHTMLFragment((HTMLFlow) event
//						.getSource()));
	}

	public void getAnnotationForWidget(HTMLFlow sender) {
		deSelectCurrentAnnotationIndex();
		Iterator<HTMLFlow> iterator = this.htmlFragments.iterator();
		int idx = -1;
		while (iterator.hasNext()) {
			idx++;
			if (iterator.next() == sender) {
				this.currentAnnotationIndex = idx;
				break;
			}
		}
		selectCurrentAnnotationIndex();
	}

	private void selectCurrentAnnotationIndex() {
		styleCurrentAnnotation("odie-Annotation-Selected");
		scrollToCurrentAnnotation(this.currentAnnotation);
	}

	private void deSelectCurrentAnnotationIndex() {
		styleCurrentAnnotation("odie-Annotation");
	}

	private void styleCurrentAnnotation(String styleName) {
		if (this.htmlFragments != null && htmlFragments.size() > 0
				&& this.currentAnnotationIndex < htmlFragments.size()) {
			HTMLFlow htmlAnnotationFragment = (HTMLFlow) this.htmlFragments
					.get(this.currentAnnotationIndex);
			htmlAnnotationFragment.setStylePrimaryName(styleName);
		}
	}

	public void scrollToCurrentAnnotation(
			ODIE_NavigatableAnnotation currentAnnotation) {
		if (currentAnnotation != null) {
			double currentScrollStartPosition = getScrollTop();
			double currentScrollOffsetHeight = getOffsetHeight();
			double currentScrollEndPosition = currentScrollStartPosition
					+ currentScrollOffsetHeight;
			double currentDisplayEndPosition = getOffsetHeight();
			double currentLine = currentAnnotation.getLineOffset();
			double totalLines = getCurrentLine();

			double position2lineRatio = 1.0d;
			if (totalLines > 0 && currentDisplayEndPosition > 0) {
				position2lineRatio = totalLines / currentDisplayEndPosition;
			}
			// Log.debug("ss, se, ds, de, al, tl, ratio = "
			// + currentScrollStartPosition + ", "
			// + currentScrollEndPosition + ", "
			// + 0.0 + ", "
			// + currentDisplayEndPosition + ", "
			// + currentLine + ", "
			// + totalLines + ", "
			// + position2lineRatio
			// ) ;
			double lineStartOffset = currentScrollStartPosition
					* position2lineRatio;
			double lineEndOffset = currentScrollEndPosition
					* position2lineRatio;
			// Log.debug("Currently displaying lines = "
			// + lineStartOffset + ", "
			// + lineEndOffset
			// ) ;
			double currentPosition = currentLine / position2lineRatio;
			if (currentLine < lineStartOffset) {
				double resetScrollStartPosition = currentPosition;
				if (resetScrollStartPosition < 0.0) {
					resetScrollStartPosition = 0.0;
				}
				// Log.debug("Backing up to position " +
				// resetScrollStartPosition) ;
				scrollTo(0, (int) resetScrollStartPosition);
			} else if (currentLine > lineEndOffset) {
				double resetScrollStartPosition = currentPosition;
				// Assume overflow is handled by the scroll panel
				// Log.debug("Scrolling forward to to position " +
				// resetScrollStartPosition) ;
				scrollTo(0, (int) resetScrollStartPosition);
			}
		}
	}

	private String extractAnnotationIdFromHTMLFragment(Canvas widget) {
		String idString = null;
		if (widget != null && widget instanceof HTMLFlow) {
			HTMLFlow fragment = (HTMLFlow) widget;
			String htmlString = fragment.getContents();
			String[] idParts = htmlString.split("[\\w-]+=");
			String annotationIdPart = idParts[1];
			String[] idSubParts = annotationIdPart.split("-");
			idString = "";
			for (int jdx = 3; jdx < idSubParts.length; jdx++) {
				idString += idSubParts[jdx] + "-";
			}
			idString = idString.substring(0, idString.length() - 1);
		}
		return idString.trim();
	}

	public int getCurrentLine() {
		return currentLine;
	}

	public void navigateToAnnotation(String id) {
		if (this.htmlFragments != null && !this.htmlFragments.isEmpty()) {
			for (HTMLFlow htmlFragment : this.htmlFragments) {
				String fragmentAnnotationId = extractAnnotationIdFromHTMLFragment(htmlFragment);
				if (fragmentAnnotationId.equals(id)) {
					getAnnotationForWidget(htmlFragment);
					break;
				}
			}
		}
	}

}
